package controllers;

import java.util.LinkedList;
import java.util.List;

import models.Action;
import models.Log;
import models.Notification;
import models.Project;
import models.Role;
import models.User;
import models.UserRoleProject;
import play.db.jpa.JPASupport;
import play.exceptions.TemplateNotFoundException;
import play.i18n.Messages;

@CRUD.For(Role.class)
public class RoleCRUD extends CRUD {

	/**
	 * Shows all projects
	 * 
	 * @author ayasa2r
	 */
	public static void showAllProjects() {
		List<Project> projects = Project.findAll();
		render(projects);
	}

	/**
	 * the method that is called when requesting to add a new system role
	 * 
	 * @author ayasa2r
	 */
	public static void blank(long inheritRole) {
		if (!Security.checkPermission("Create System Role", null))
			forbidden();
		// getting the role that needs to be inherited
		Role r = Role.findById(inheritRole);
		// changing the lists according to the inheritance
		List<Role> rList = Role.find("byIsSystem", true).fetch();
		List<Action> aList = Action.findAll();
		List<Action> aSelList = r.actions;
		// rendering the page accordingly
		render(inheritRole, rList, aList, aSelList, r);
	}

	/**
	 * the method that is called when requesting to inherit from another role
	 * certain permissions
	 * 
	 * @author ayasa2r
	 * @param inheritRole
	 *            the role that is going to inherit the actions from
	 */
	public static void blankWithInheritance(long inheritRole) {
		if (!Security.checkPermission("Create System Role", null))
			forbidden();
		// getting the role that needs to be inherited
		Role r = Role.findById(inheritRole);
		// changing the lists according to the inheritance
		List<Role> rList = Role.find("byIsSystem", true).fetch();
		List<Action> aList = Action.findAll();
		List<Action> aSelList = r.actions;
		// rendering the page accordingly
		render(inheritRole, rList, aList, aSelList, r);
	}

	public static void create() throws Exception {
		ObjectType type = ObjectType.get(getControllerClass());
		notFoundIfNull(type);
		JPASupport object = type.entityClass.newInstance();
		validation.valid(object.edit("object", params));
		Role r = Role.find("byIsSystemAndTitle", true, ((Role) object).title)
		.first();
		if (validation.hasErrors()) {
			renderArgs.put("error", Messages.get("crud.hasErrors"));
			try {
				render(request.controller.replace(".", "/") + "/blank.html",
						type,r);
			} catch (TemplateNotFoundException e) {
				render("CRUD/blank.html", type,r);
			}
		}
		
		List<Role> rList = Role.find("byIsSystem", true).fetch();
		List<Action> aList = Action.findAll();
		List<Action> aSelList = null;
		if (r != null) {
			flash.error("A role with this name already exists in the System.");
			render("RoleCRUD/blank.html", rList, aList, aSelList,r);
		}

		object.save();
		List<Project> projects = Project.findAll();
		for (int i = 0; i < projects.size(); i++) {
			Role temp = (Role) object;
			Role actual = new Role(((Role) object).title, false);
			for (int j = 0; j < temp.actions.size(); j++)
				actual.actions.add(temp.actions.get(j));
			actual.save();
			UserRoleProject urp = new UserRoleProject(true, null, actual,
					projects.get(i)).save();
			User proAd = UserRoleProject
			.find(
					"select urp.user from UserRoleProject urp where urp.project=? and urp.role.title=?",
					projects.get(i), "Project Admin").first();
			new Log(Long.parseLong(session.get("userId")), (Long) urp
					.getEntityId(), UserRoleProject.class.getName(), session
					.get("userId")
					+ " created the role "
					+ actual.title
					+ " in the project "
					+ projects.get(i).name + ".", (Long) projects.get(i)
					.getEntityId(), "").save();

		}List<User> proAd = UserRoleProject
		.find(
				"select urp.user from UserRoleProject urp where urp.role.title=?", "Project Admin").fetch();
		
		Notifications.notifyAllUsers(proAd, "New Role in System!", "A new role is added in the system and so your project now has this new role in its list of roles, check it out ;)");
		new Log(Long.parseLong(session.get("userId")), (Long) ((Role) object)
				.getEntityId(), Role.class.getName(), session.get("userId")
				+ " created the role " + ((Role) object).title, "").save();
		if (params.get("_save") != null) {
			flash.success("You have successfully created a system role.");
			redirect(request.controller + ".list");
		}
		if (params.get("_saveAndAddAnother") != null) {
			flash.success("You have successfully created a system role.");
			long sysAd = Role.find("select r.id from Role r where r.title=?","System Admin").first();
			blank(sysAd);
		}
		if (params.get("_saveAndContinue") != null) {
			flash.success("You have successfully created a system role.");
			redirect(request.controller + ".show", object.getEntityId());
		}
	}

	/**
	 * method inherited from CRUD and represents the list of system Roles in the
	 * system
	 * 
	 * @param page
	 * @param search
	 * @param searchFields
	 * @param orderBy
	 * @param order
	 */
	public static void list(int page, String search, String searchFields,
			String orderBy, String order) {
		ObjectType type = ObjectType.get(getControllerClass());
		notFoundIfNull(type);
		if (page < 1) {
			page = 1;
		}
		List<JPASupport> objects = type.findPage(page, search, searchFields,
				orderBy, order, (String) request.args.get("where"));
		Long count = type.count(search, searchFields, (String) request.args
				.get("where"));
		Long totalCount = type.count(null, null, (String) request.args
				.get("where"));
		List<Role> rList = Role.find("byIsSystem", true).fetch();
		Role r = Role.find("byTitle", "System Admin").first();

		try {
			render(type, objects, count, totalCount, page, orderBy, order,
					rList, r);
		} catch (TemplateNotFoundException e) {
			render("CRUD/list.html", type, objects, count, totalCount, page,
					orderBy, order, rList, r);
		}
	}

	/**
	 * Shows the details of the system role required to be edited
	 * 
	 * @param roleId
	 *            the required role
	 */

	public static void showEditRole(long roleId) { 
		// getting the role that needs to be edited 
		Role r = Role.findById(roleId); 
		// checking if being hacked 
		if (r == null || r.isSystem == false)
			forbidden("You are a Hacker!"); // showing the details of the role toedit
		String title = r.title; 
		List<Role> rList = Role.find("byIsSystem",true).fetch(); 
		List<Action> aList = Action.findAll();
		List<Action>
		aSelList = r.actions; // rendering the page accordingly
		render(title,roleId, rList, aList, aSelList);

	}
	/**
	 * Shows the details of the edited system role after inheritance
	 * 
	 * @param title
	 *            the title of the edited role
	 * @param roleId
	 *            the required role
	 * @param inheritRole
	 *            the required role to inherit the actions from
	 */

	public static void inheritEditRole(String title, long roleId, long inheritRole) { 
		// getting the role that needs to be inherited 
		Role r =Role.findById(inheritRole); 
		// checking if being hacked 
		if (r == null ||r.isSystem == false) 
			forbidden("You are a Hacker!"); 
		// changing the lists according to the inheritance
		List<Role>  rList =Role.find("byIsSystem", true).fetch(); List<Action> aList =
			Action.findAll(); 
		List<Action> aSelList = r.actions; 
		// rendering the page accordingly 
		render(title, roleId, rList, r, aList, aSelList);

	}
	/**
	 * Edits the system role
	 * 
	 * @param title
	 *            the title of the edited role
	 * @param roleId
	 *            the edited role
	 * @param action
	 *            the actions of the edited role
	 */

	public static void edited(String title, long roleId, long[] action) { 
		//getting the role that needs to be edited 
		Role r = Role.findById(roleId);
		// checking if being hacked
		if (r == null ||r.isSystem == false) 
			forbidden("You are a Hacker!"); // editing the role
		r.title = title; 
		r.isSystem = true; 
		List<Action> editActions = new LinkedList<Action>(); 
		for (int i = 0; i < action.length; i++) {
			editActions.add((Action) Action.findById(action[i]));
		}
		r.actions = editActions;
		//saving the edited role 
		r.save(); 
		// rendering the page accordingly
		render(); }


	/**
	 * Shows the details of the system role required to be edited
	 * 
	 * @param id
	 *            the required role
	 */
	public static void show(long id) {
		// Security Checking
		if (!Security.checkPermission("Edit System Role", null))
			forbidden();
		// showing the details of the role to edit
		Role myRole = Role.findById(id);
		// checking if being hacked
		if (myRole == null || myRole.isSystem == false)
			forbidden("You are a Hacker!");
		List<Role> rList = Role.find("byIsSystem", true).fetch();
		List<Action> aList = Action.findAll();
		List<Action> aSelList = myRole.actions;
		// rendering the page accordingly
		render(myRole, rList, aList, aSelList);
	}

	/**
	 * Shows the details of the edited system role after inheritance
	 * 
	 * @param myRoleId
	 *            the required role
	 * @param inheritRole
	 *            the required role to inherit the actions from
	 */
	public static void showWithInheritance(long myRoleId, long inheritRole) {
		// Security Checking
		if (!Security.checkPermission("Edit System Role", null))
			forbidden();
		// getting the role that needs to be edited
		Role r = Role.findById(inheritRole);
		// checking if being hacked
		if (r == null || r.isSystem == false)
			forbidden("You are a Hacker!");
		// changing the lists according to the inheritance
		List<Role> rList = Role.find("byIsSystem", true).fetch();
		List<Action> aList = Action.findAll();
		List<Action> aSelList = r.actions;
		// rendering the page accordingly
		Role myRole = Role.findById(myRoleId);
		// checking if being hacked again
		if (myRole == null || myRole.isSystem == false)
			forbidden("You are a Hacker!");
		render(myRole, inheritRole, rList, aList, aSelList);
	}

	/**
	 * Saves the edited system role
	 * 
	 * @param id
	 *            the edited role
	 */
	public static void save(long id) throws Exception {
		ObjectType type = ObjectType.get(getControllerClass());
		notFoundIfNull(type);
		JPASupport object = type.findById(id);
		String originalRoleTitle = ((Role) object).title;
		validation.valid(object.edit("object", params));
		if (validation.hasErrors()) {
			renderArgs.put("error", Messages.get("crud.hasErrors"));
			try {
				render(request.controller.replace(".", "/") + "/show.html",
						type, object);
			} catch (TemplateNotFoundException e) {
				render("CRUD/show.html", type, object);
			}
		}

		String newRoleTitle = ((Role) object).title;

		// checking the availability of the new title
		// First checking the role still have the same title
		if (!originalRoleTitle.equals(newRoleTitle)) {
			Role r = Role.find("byIsSystemAndTitle", true,
					((Role) object).title).first();

			// checking if there is already a role with that name
			if (r != null) {
				flash
				.error("A role with this name already exists in the System.");
				redirect(request.controller + ".show", object.getEntityId());
			}
		}

		// Passed title availability check
		object.save();
		// LOG
		new Log(Long.parseLong(session.get("userId")), null, null,
				"Edited a System Role", null).save();
		flash.success("You have successfully edited a system role.");
		if (params.get("_save") != null) {
			redirect(request.controller + ".list");
		}
		redirect(request.controller + ".show", object.getEntityId());
	}

	/**
	 * Deletes a role from the system
	 * 
	 * @param id
	 *            id of role to be deleted
	 */
	public static void delete(String id) {
		ObjectType type = ObjectType.get(getControllerClass());
		notFoundIfNull(type);
		JPASupport object = type.findById(id);
		try {
			object.delete();
			flash.success("Role successfully deleted !");
		} catch (Exception e) {
			flash.error(Messages.get("crud.delete.error", type.modelName,
					object.getEntityId()));
			redirect(request.controller + ".show", object.getEntityId());
		}
		redirect(request.controller + ".list");
	}
}
